﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Runtime.Serialization;
using System.Runtime;
using System.Reflection;

namespace LevelEditor
{
    /// <summary>
    /// Base class for all scene objects
    /// </summary>
    public abstract class Entity : ISerializable
    {
        /// <summary>
        /// Vertecies used to draw world transform handles
        /// </summary>
        private static VertexPositionColor[] Line = new VertexPositionColor[2]
        {
            new VertexPositionColor(Vector3.Zero, Color.Black),
            new VertexPositionColor(Vector3.Right, Color.Black)
        };


        /// <summary>
        /// Gets or sets a matrix that defines the world transform of the mesh
        /// </summary>
        public virtual Matrix World
        {
            get { return mesh.World; }

            set { mesh.World = value; }
        }

        /// <summary>
        /// The mesh that represents this object
        /// </summary>
        public Mesh mesh;

        /// <summary>
        /// A value indicating the entity has been selected by the user
        /// </summary>
        public bool selected;

        /// <summary>
        /// Gets or sets the position of the entity
        /// </summary>
        public Vector3 Position
        {
            get { return new Vector3(World.M41, World.M42, World.M43); }

            set
            {
                Matrix w = World;
                w.M41 = value.X;
                w.M42 = value.Y;
                w.M43 = value.Z;
                World = w;
            }
        }

        /// <summary>
        /// The bounding box for the x axis control
        /// </summary>
        protected BoundingBox TransformXControl
        {
            get { return new BoundingBox(Position - Vector3.UnitY * 0.2f, Position + Vector3.Right + Vector3.UnitY * 0.2f); }
        }

        /// <summary>
        /// The bounding box for the y axis control
        /// </summary>
        protected BoundingBox TransformYControl
        {
            get { return new BoundingBox(Position - Vector3.UnitZ * 0.2f, Position + Vector3.Up * 5 + Vector3.UnitZ * 0.2f); }
        }

        /// <summary>
        /// The type this object spawns in the game engine
        /// </summary>
        public string typename;

        /// <summary>
        /// The bounding box for the z axis control
        /// </summary>
        protected BoundingBox TransformZControl
        {
            get { return new BoundingBox(Position - Vector3.UnitY * 0.2f, Position + Vector3.Backward * 5 + Vector3.UnitZ * 0.2f); }
        }

        public event System.Windows.Forms.MouseEventHandler TransformYAxis;
        public event System.Windows.Forms.MouseEventHandler TransformXAxis;
        public event System.Windows.Forms.MouseEventHandler TransformZAxis;

        public Entity(string typename)
        {
            MainMenu.instance.viewWindow1.MouseMove += viewWindow1_MouseDown;
            this.typename = typename;
        }

        void viewWindow1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (!selected || e.Button != System.Windows.Forms.MouseButtons.Left)
                return;

            Ray cameraRay = Camera.instance.ComputeRay(MainMenu.instance.viewWindow1.viewport, e.X, e.Y);
            if (cameraRay.Intersects(TransformXControl) != null && TransformXAxis != null)
                TransformXAxis(this, e);
            else if (cameraRay.Intersects(TransformYControl) != null && TransformYAxis != null)
                TransformYAxis(this, e);
            else if (cameraRay.Intersects(TransformZControl) != null && TransformZAxis != null)
                TransformZAxis(this, e);
        }

        /// <summary>
        /// Gets graphics object for child classes
        /// </summary>
        protected Graphics graphics
        {
            get { return MainMenu.instance.graphics; }
        }

        /// <summary>
        /// Updates the object by the provided ammount of time
        /// </summary>
        /// <param name="dt">The ammount of time that has elapsed since the last update</param>
        public virtual void Update(float dt)
        {

        }

        /// <summary>
        /// Draws the object to the screen
        /// </summary>
        public virtual void Draw()
        {
            // draw world handles if selected
            if (selected)
            {
                // disable the depth stencil for this
                MainMenu.instance.graphics.device.DepthStencilState = DepthStencilState.None;
                DrawLine(World, Color.Yellow);
                DrawLine(Matrix.CreateRotationZ(MathHelper.ToRadians(90)) * World, Color.Red);
                DrawLine(Matrix.CreateRotationY(MathHelper.ToRadians(90)) * World, Color.Blue);
                MainMenu.instance.graphics.device.DepthStencilState = DepthStencilState.Default;
            }
        }

        /// <summary>
        /// Draws a line to viewWindow1
        /// </summary>
        /// <param name="world">The world transform of the line</param>
        private static void DrawLine(Matrix world, Color color)
        {
            Effect e = EffectManager.FlatShader;
            Camera.instance.FormatEffect(e);
            e.Parameters["World"].SetValue(world);
            e.Parameters["color"].SetValue(color.ToVector4());
            e.CurrentTechnique.Passes[0].Apply();
            MainMenu.instance.graphics.device.DrawUserPrimitives(PrimitiveType.LineList, Line, 0, 1);
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AssemblyName = Assembly.GetAssembly(typeof(Entity)).ToString();
            info.FullTypeName = this.GetType().FullName;
        }
    }
}